import React from 'react';
import { Input, Form, Col, Row, Button, List } from 'antd';
import classnames from 'classnames';
import { DownOutlined, UpOutlined } from '@ant-design/icons';

/**
 * 参数
 * columns 数据骨架Object
 * initialValues 表单初始值
 * layout 布局
 *   layout.gutter 每个col的间距
 *   layout.column 没行有几列
 * itemLayout 控制Item的lable和Input所占宽度
 * onFinish 结束
 * onFinishFailed 校验失败
 * onReset 重置回调
 * shouldChangUpdate 值改变后是否重新渲染
 * getFormRef 获取form引用
 * scrollToFirstError 滚动到第一个错误弹框
 * onSearch 表单提交时的回调
 * onReset 表单重置的回调
 * columns.title 显示的标题
 * columns.key 字段名称
 * columns.initialValue 默认值
 * columns.render 渲染组件，默认为Input组件
 *  *
 * xs 屏幕 < 576px
 * sm 屏幕 ≥ 576px
 * md 屏幕 ≥ 768px
 * lg 屏幕 ≥ 992px
 * xl 屏幕 ≥ 1200px
 * xxl 屏幕 ≥ 1600px
 */

const ItemLayout = {
  labelCol: {
    span: 8,
  },
  wrapperCol: {
    span: 16,
  },
};

interface IRule {
  required: boolean;
  message?: string;
  validator?: (...args: any[]) => Promise<any>;
  [k: string]: any;
}

interface ColumnProps {
  title: string; // Item的label
  key: string; // Item的name
  render?: (...args: any[]) => React.ReactNode;
  placeholder?: string;
  layout?: Record<string, unknown>;
  rules?: IRule[] | ((...args: any[]) => IRule[]);
  initialValue?: any;
  appendLastRow?: boolean; // 内部使用
  isHidden?: (...args: any[]) => boolean; // 是否隐藏
}

interface IProps {
  className?: string;
  columns: ColumnProps[];
  maxRows: number; // 初始化展示的最大行数，超出隐藏
  initialValues?: Record<string, unknown>; // 初始值 优先级更高
  submitBtnText?: string;
  resetBtnText?: string;
  buttonWrapStyle?: React.CSSProperties;
  hiddenResetBtn?: boolean;
  addonButton?: () => React.ReactNode;
  loading?: boolean;
  onSearch: (...args: any[]) => void;
  onFinishFailed?: (...args: any[]) => void;
  getFormRef?: (ref: React.RefObject<any>) => void;
  onReset: (...args: any[]) => void;
  shouldChangUpdate?: boolean;

  itemLayout?: {
    // 控制Item
    labelCol: { span: number };
    wrapperCol: { span: number };
  };
  layout: { gutter: number; column: number };
  btnsAlign?: 'left' | 'center' | 'right' | 'inline';
  showDivider?: boolean; // 按钮上方 显示分割线
}

interface IState {
  initialFileds: any;
  down: boolean;
}

class SearchPannel extends React.Component<IProps, IState> {
  static defaultProps = {
    maxRows: 2,
    columns: [],
    layout: { gutter: 15, column: 4 },
    itemLayout: ItemLayout,
    btnsAlign: 'right',
  };

  formRef: React.RefObject<any> = React.createRef();

  constructor(props: IProps) {
    super(props);
    const initialValues = (props.columns || []).reduce((memo: any, item) => {
      memo[item.key] = item.initialValue;
      return memo;
    }, {});

    this.state = {
      down: false,
      initialFileds: { ...initialValues, ...props.initialValues },
    };
  }

  componentDidMount() {
    const { getFormRef } = this.props;
    getFormRef && getFormRef(this.formRef);
    document.addEventListener('keydown', this.onSearchKeyDown);
  }

  componentWillUnmount() {
    document.removeEventListener('keydown', this.onSearchKeyDown);
  }

  createItem = ({
    title,
    render,
    key,
    placeholder,
    layout = {},
    rules,
    isHidden,
    ...rests
  }: ColumnProps) => {
    const fieldsValue = (this.formRef.current && this.formRef.current.getFieldsValue(true)) || {};

    if (typeof isHidden === 'function' && isHidden(fieldsValue)) {
      // 判断是否隐藏
      return null;
    }

    // render默认为input输入框
    if (!render) {
      render = () => <Input placeholder={placeholder || `请输入${title}`} />;
    }

    // 获取渲染得到的组件
    const comp = render(fieldsValue || {}, this.formRef.current);
    if (!comp) return null;

    return (
      <Form.Item
        {...layout}
        {...rests}
        label={title}
        name={key}
        className={`form-item__${key}`}
        key={key}
        rules={typeof rules === 'function' ? rules(fieldsValue) : rules}
      >
        {comp}
      </Form.Item>
    );
  };

  // 提交查询
  onFinish = (values: any) => {
    const { onSearch } = this.props;
    // 过滤undefined 和空格
    Object.keys(values).forEach(key => {
      if ([undefined].includes(values[key])) {
        delete values[key];
      } else if (typeof values[key] === 'string') {
        values[key] = values[key].trim();
      }
    });

    onSearch && onSearch(values);
  };

  // 重置
  onReset = () => {
    this.formRef.current.resetFields();
    this.props.onReset && this.props.onReset();
  };

  onSearchKeyDown = (event: any) => {
    if (event.code === 'Enter') {
      // 点击回车，提交表单进行查询
      this.formRef.current.submit();
    }
  };

  // 渲染底部按钮
  renderSubmit() {
    const {
      submitBtnText = '查询',
      resetBtnText = '重置',
      buttonWrapStyle = {},
      hiddenResetBtn,
      addonButton,
      columns,
      maxRows,
      layout,
      loading,
      btnsAlign,
      showDivider,
    } = this.props;
    const { down } = this.state;

    const maxCount = maxRows * layout.column + 1;

    return (
      <Row
        gutter={24}
        className={classnames('com-search-panel__submit', {
          'show-divider': showDivider,
        })}
        // @ts-ignore
        style={{ textAlign: btnsAlign, ...buttonWrapStyle }}
      >
        <Col span={24}>
          <Button
            type="primary"
            onKeyDown={this.onSearchKeyDown}
            htmlType="submit"
            loading={loading}
          >
            {submitBtnText}
          </Button>
          {!hiddenResetBtn && (
            <Button type="default" onClick={this.onReset} disabled={loading}>
              {resetBtnText}
            </Button>
          )}
          {/* 挂载外部传入按钮 */}
          {addonButton && addonButton()}
          {columns.length > maxCount && (
            <span
              className="com-search-panel__up-down"
              onClick={() => this.setState({ down: !down })}
            >
              {down ? '收起 ' : '展开 '}
              {down ? <DownOutlined /> : <UpOutlined />}
            </span>
          )}
        </Col>
      </Row>
    );
  }

  onValuesChange = () => {
    const { shouldChangUpdate } = this.props;
    shouldChangUpdate && this.forceUpdate();
  };

  render() {
    const { initialFileds, down } = this.state;
    const {
      columns = [],
      className,
      layout,
      itemLayout,
      maxRows,
      btnsAlign,
      onFinishFailed,
    } = this.props;

    const maxCount = maxRows * layout.column - 1;

    // 最后一行没排满时 在最后一行追加提交按钮
    const appendLastRow = btnsAlign === 'inline';
    const appendLastCols = columns.slice();
    appendLastCols.push({ appendLastRow: true } as ColumnProps);
    return (
      <div className={classnames('com-search-panel', className)}>
        <Form
          {...itemLayout}
          onFinishFailed={onFinishFailed}
          onFinish={this.onFinish}
          ref={this.formRef}
          onValuesChange={this.onValuesChange}
          initialValues={initialFileds}
        >
          <List
            grid={layout}
            dataSource={!appendLastRow ? columns : appendLastCols}
            renderItem={(item: ColumnProps, i) => (
              <List.Item style={{ display: !down && i > maxCount ? 'none' : 'block' }}>
                {appendLastRow && item.appendLastRow ? this.renderSubmit() : this.createItem(item)}
              </List.Item>
            )}
          />
          {!appendLastRow && this.renderSubmit()}
        </Form>
      </div>
    );
  }
}

export default SearchPannel;
